"""
xml2py.py	Convert XML to Python

Relies on ElementTree for the XML parsing.  
This is based on pesterfish.py but uses a different mapping.
Based on XML->JSON mapping is described at
http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html
written by R. White, 2006 November 6
modified by Massimo Di Pierro
This is very similar to the mapping used for Yahoo Web Services
(http://developer.yahoo.com/common/json.html#xml).
"""

# I'm not sure whether these alternatives actually work since
# I grabbed them from pesterfish, but I want ElementTree instead
# of just Element.

import re
import xml.etree.cElementTree as et

regex_clean=re.compile('\{.*?\}')

def elem_to_internal(elem,strip=1,zero=False):
    """Convert an Element into an internal dictionary"""

    d = {}

    # loop over subelements to merge them
    for subelem in elem:
        v = elem_to_internal(subelem,strip=strip)
        tag = subelem.tag
        value = v[tag]
        if isinstance(value,str):
            try:
                value = int(value)
            except ValueError:
                try:
                    value = float(value)
                except ValueError:
                    pass
        tag = regex_clean.sub('',tag)
        try:
            # add to existing list for this tag
            d[tag].append(value)
        except AttributeError:
            # turn existing entry into a list
            d[tag] = [d[tag], value]
        except KeyError:
            # add a new non-list entry
            d[tag] = value
    text = elem.text
    tail = elem.tail
    if strip:
        # ignore leading and trailing whitespace
        if text: text = text.strip()
        if tail: tail = tail.strip()

    if tail:
        d['#tail'] = tail

    if d:
        # use #text element if other attributes exist
        if text: d["#text"] = text
    else:
        # text is the value if no attributes
        d = text or None    
    if zero:
        return {regex_clean.sub('',elem.tag): d}
    return {elem.tag: d}

'''
def internal_to_elem(pfsh, factory=et.Element):

    """Convert an internal dictionary into an Element.

    Whatever Element implementation we could import will be
    used by default; if you want to use something else, pass the
    Element class as the factory parameter.
    """

    attribs = {}
    text = None
    tail = None
    sublist = []
    tag = pfsh.keys()
    if len(tag) != 1:
        raise ValueError("Illegal structure with multiple tags: %s" % tag)
    tag = tag[0]
    value = pfsh[tag]
    if isinstance(value,dict):
        for k, v in value.items():
            if k[:1] == "@":
                attribs[k[1:]] = v
            elif k == "#text":
                text = v
            elif k == "#tail":
                tail = v
            elif isinstance(v, list):
                for v2 in v:
                    sublist.append(internal_to_elem({k:v2},factory=factory))
            else:
                sublist.append(internal_to_elem({k:v},factory=factory))
    else:
        text = value
    e = factory(tag, attribs)
    for sub in sublist:
        e.append(sub)
    e.text = text
    e.tail = tail
    return e
'''

def elem2py(elem, strip=1):

    """Convert an ElementTree or Element into a JSON string."""

    if hasattr(elem, 'getroot'):
        elem = elem.getroot()
    return elem_to_internal(elem,strip=strip,zero=True)


def xml2py(xmlstring,strip=1):

    """Convert an XML string into a JSON string."""

    elem = et.fromstring(xmlstring)
    return elem2py(elem,strip=strip)
